//voxel system



/*
//creates a polygonal mesh from an isosurface
//the mesh is returned ready for use by threejs
//the dual contouring method is used as described here:
//http://www.oocities.org/tzukkers/isosurf/isosurfaces.html
var mesher_DC = function() {

    var t = this;
    this.voxel_size = new THREE.Vector3(16,16,16);

    this.voxel = function() {
        return {
            index: 0
            ,point: new THREE.Vector3()//point in mesh
            ,mat: new THREE.MeshBasicMaterial({color:0xffff00})
        }
    }

    //a y-slice of points of the voxel grid
    this.row = function() {
        return {
            vector: new THREE.Vector3()
            ,points: []
            ,densities: [],
            ,voxels: []
        }
    }   

    

    //creates a mesh from an isosurface
    this.buildMesh = function(iso) {
        if(!iso) {
            console.error('mesher: passed null iso');
        }

        var bbox = iso.aabb;
        var xmin, ymin, zmin;
        var xmax, ymax, zmax;
        xmin = Math.floor(bbox.min.x);
        ymin = Math.floor(bbox.min.y);
        zmin = Math.floor(bbox.min.z);
        xmax = Math.ceil(bbox.max.x + 3 * t.voxel_size.x);
        ymax = Math.ceil(bbox.max.y + 3 * t.voxel_size.y);
        zmax = Math.ceil(bbox.max.z + 3 * t.voxel_size.z);

        //size of mesh in voxels
        xsize = (xmax - xmin) / t.voxel_size.x + 1;
        ysize = (ymax - ymin) / t.voxel_size.y + 1;
        zsize = (zmax - zmin) / t.voxel_size.z + 1;
        zsize = ((zsize + 3) >> 2) << 2;//round to multiple of 4
        
        if(xsize < 1 || ysize < 2 || zsize < 1) {
            console.error('mesher: invalid isosurface buildMesh failed!');
        }       

    }

}
*/

var doll = (function(_doll) {
    
    _doll.voxel = _doll.voxel || {};

    _doll.voxel.block_size = 25;

    _doll.voxel.block_material = function (mat) {
            _doll.voxel.material = mat || new THREE.MeshBasicMaterial({color: 0x0000ff});
    }

    _doll.voxel.block_geometry = function (geo) {
        _doll.voxel.geometry = geo || new THREE.CubeGeometry(_doll.voxel.block_size,_doll.voxel.block_size,_doll.voxel.block_size);
    }

    _doll.voxel.init = function(geometry, material) {
        _doll.voxel.block_material();
    }

    //types
    _doll.voxel.DEFAULT =  0;
    _doll.voxel.GRASS   =  1;
    _doll.voxel.WATER   =  2;
    
    _doll.voxel.type = [
        _doll.voxel.DEFAULT
        ,_doll.voxel.GRASS
        ,_doll.voxel.WATER
    ];

    //blocks and chunks...
    _doll.voxel.block = function(type) { 
        this.on = false;
        this.type = type || _doll.voxel.DEFAULT;
        this.chunkID = 0;
    }
    _doll.voxel.chunk_size = 16;
    _doll.voxel.chunk = function() {
        //3d array change to Uint8Array later
        //this.blocks = new Uint8Array(_doll.voxel.chunk_size);
        this.blocks = [[[]]];
        this.blocks[0][0][0] = null;
        this.size = _doll.voxel.chunk_size;
        this.size2 = this.size*this.size;

        //management flags
        this.isLoaded = false;
        this.isSetup = false;
        this.isEmpty = true;
        this.isVisible = true;
        this.isImmutable = false;

        var t = this;
        this.destroy = function() {
            t.blocks = null;
        }

        this.load = function() {
            t.fill();            
        }

        this.unload = function() {
            t.fill();            
        }

        this.setup = function() {
        }

        this.fill = function() {
            for(var x=0; x < t.size; x++) {
                t.blocks[x] = [];
                for(var y=0; y < t.size; y++) {
                    t.blocks[x][y] = [];
                    for(var z=0; z < t.size; z++) {
                        t.blocks[x][y][z] = new doll.voxel.block();
                    }
                }
            }
        }

        this.block = function(x,y,z) {
            //var i = x + y*this.size + z*this.size2;
            //return this.blocks[i];
            return t.blocks[x][y][z];
        }

        this.geo = null;
        this.mesh = null;
        this.numblock = 0;
        //create a mesh if needed...
        this.buildmesh = function() {
            if(t.mesh) {
                return t.mesh;
            }

            t.numblock = 0;
            t.geo = new THREE.Geometry();
            for(var x=0; x < t.size; x++) {
                for(var y=0; y < t.size; y++) {
                    for(var z=0; z < t.size; z++) {
                        if(t.blocks[x][y][z] && t.blocks[x][y][z].on) {
                            t.createBlock(x,y,z);
                        }
                    }
                }
            }

            t.geo.mergeVertices();
            t.geo.computeFaceNormals();
            t.geo.computeBoundingBox();

            t.mesh = new THREE.Mesh(t.geo, _doll.voxel.material);
        }

        this.createBlock = function(x,y,z) {
            var bs = _doll.voxel.block_size;
            var bn = t.numblock*8;

            //*2 as long as block_size is actually half_block_size
            x *= bs*2;
            y *= bs*2;
            z *= bs*2;

            t.geo.vertices.push(new THREE.Vector3(x-bs, y-bs, z+bs));
            t.geo.vertices.push(new THREE.Vector3(x+bs, y-bs, z+bs));
            t.geo.vertices.push(new THREE.Vector3(x+bs, y+bs, z+bs));
            t.geo.vertices.push(new THREE.Vector3(x-bs, y+bs, z+bs));
            t.geo.vertices.push(new THREE.Vector3(x+bs, y-bs, z-bs));
            t.geo.vertices.push(new THREE.Vector3(x-bs, y-bs, z-bs));
            t.geo.vertices.push(new THREE.Vector3(x-bs, y+bs, z-bs));
            t.geo.vertices.push(new THREE.Vector3(x+bs, y+bs, z-bs));

            //front
            t.geo.faces.push(new THREE.Face4(0+bn,1+bn,2+bn,3+bn));
            //back
            t.geo.faces.push(new THREE.Face4(4+bn,5+bn,6+bn,7+bn));
            //right
            t.geo.faces.push(new THREE.Face4(1+bn,4+bn,7+bn,2+bn));
            //left
            t.geo.faces.push(new THREE.Face4(5+bn,0+bn,3+bn,6+bn));
            //top
            t.geo.faces.push(new THREE.Face4(3+bn,2+bn,7+bn,6+bn));
            //bottom
            t.geo.faces.push(new THREE.Face4(5+bn,4+bn,1+bn,0+bn));
            t.numblock++;
        }

        this.setup_sphere = function () {
            for (var z=0; z<t.size; z++)
            {
                for (var y=0; y<t.size; y++)
                {
                    for (var x=0; x<t.size; x++)
                    {
                        if (Math.sqrt((x-t.size/2)*(x-t.size/2) + (y-t.size/2)*(y-t.size/2) + (z-t.size/2)*(z-t.size/2)) <= t.size/2)
                        {
                            t.blocks[x][y][z].on = true;
                            //t.block[x][y][z].type = GRASS;
                        }
                    }
                }
            }
        }
    }//end chunk

    //chunk management lists

    //chunks that need to be loaded
    _doll.voxel.chunkload = [];
    //chunks that should be rendered this frame
    //visible after occulsion,frustum,empty checks
    _doll.voxel.chunkrender = [];
    //chunks that need to be unloaded
    _doll.voxel.chunkunload = [];
    //chunks that are visible
    _doll.voxel.chunkvisible = [];
    _doll.voxel.checkvisibility = true;
    //chunks that need to be setup
    _doll.voxel.chunksetup = [];
    //manages the chunks
    _doll.voxel.oldcam_pos = null;
    _doll.voxel.oldcam_rotation = null;
    _doll.voxel.update = function (delta) {
        _doll.voxel.load_chunks();
        _doll.voxel.setup_chunks();
        _doll.voxel.rebuild_chunks();
        _doll.voxel.unload_chunks();
        _doll.voxel.visible_chunks();
        if(_doll.voxel.oldcam != _doll.camera.position || _doll.voxel.oldcam_rotation != _doll.camera.quaternion) {
            _doll.voxel.renderable_chunks();
        }
        _doll.voxel.oldcam = _doll.camera.position;
        _doll.voxel.oldcam = _doll.camera.quaternion;
    }
    _doll.voxel.MAX_CHUNK_LOAD = 5;
    _doll.voxel.load_chunks = function() {
        var loaded=0;

        for(var i=0; i<_doll.voxel.chunkload.length; i++) {
            if(!_doll.voxel.chunkload[i].loaded) {
                _doll.voxel.chunkload[i].load();
                loaded++;
                _doll.voxel.check_visibility = true;
            }
            
            if(loaded > _doll.voxel.MAX_CHUNK_LOAD) {
                break;
            }
        }
        
        _doll.voxel.chunkload = [];
    }

    _doll.voxel.setup_chunks = function() {
        for(var i=0; i<_doll.voxel.chunksetup.length; i++) {
            if(_doll.voxel.chunksetup[i].isLoaded && !_doll.voxel.chunksetup[i].isSetup) {
                _doll.voxel.chunksetup[i].setup();
                if(_doll.voxel.chunksetup[i].isSetup) {
                    _doll.voxel.check_visibility = true;
                }
            }
        }

        _doll.voxel.chunksetup = [];
    }

    _doll.voxel.rebuild_chunks = function () {

    }

    _doll.voxel.unload_chunks = function () {
        for(var i=0; i<_doll.voxel.chunkunload.length; i++) {
            if(_doll.voxel.chunkunload[i].isLoaded) {
                _doll.voxel.chunkunload[i].unload();
                _doll.voxel.check_visibility = true;
            }
        }
        _doll.voxel.chunkunload = [];
    }

    //use a scenegraph to decide this
    _doll.voxel.visible_chunks = function () {
        _doll.voxel.chunkvisible = [];
    }

    //add these chunks to the scene to be rendered
    //if they pass occulsion and frustrum checks
    _doll.voxel.renderable_chunks = function () {
        _doll.voxel.chunkrender = [];
    }


    //sparse voxel octree approach with notes as I learn

    //root node represents the entire scene and each child represents
    //an eighth of its volume and so forth for each node
    //each node has either zero or eight children
    //nodes with children are internal nodes
    //nodes without children are leaf nodes

    _doll.voxel.node = function() {
        this.children = [];
        this.make_children = function() {
            for(var i=0; i>8; i++) {
                this.children[i] = new _doll.voxel.node();
            }
        }

        //here decide if children should be made or not by performing
        //intersection tests with geometry

    }



    return _doll;
}(doll || {}));
